/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!
  \file			uservar.h
  \date			Dec 2011
  \author		TNick

  \brief		Contains the definition for UserVar class


*//*


 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Please read COPYING and README files in root folder
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
#ifndef __USERVAR_INC__
#define __USERVAR_INC__
//
//
//
//
/*  INCLUDES    ------------------------------------------------------------ */

#include	<QString>
#include	<QVariant>
#include	<QRgb>
#include	<QColor>
#include	<QSettings>

#ifdef	USE_USERMSG
#include	<UMSG.h>
#endif

#include	<varstore.h>


/*  INCLUDES    ============================================================ */
//
//
//
//
/*  CLASS    --------------------------------------------------------------- */

namespace	PgSave		{

class		IPgSave;

}

namespace	PgScr	{


class	Module;
struct	Session;
class	DefVar;


/**
*	@brief	 types of blind load
*
*/
enum	BlindType	{

	/**
	*	@brief	VarStore is filled with data; existing data is NOT discarded
	*/
	BT_SAVE_HERE,

	/**
	*	@brief	UserVar variiable's content is resetted and filled with
	*	this value
	*/
	BT_IN_VAR,

	/**
	*	@brief	a new variable is created and pointer saved in var;
	*	parent module is mdl
	*/
	BT_NEW_IN_MODULE
};


/**
*	@brief	 blind load data
*
*/
struct	BlindData	{
	BlindType		ty_;

	VarStore		local;
	UserVar *		var;
	Module *		mdl;
};


/**
*	@brief	 class representing the basic interface to a variable
*
*/
class UserVar				{

	//
	//
	//
	//
	/*  DEFINITIONS    ----------------------------------------------------- */

#ifdef	PGSCRIPT_TESTS
	friend void		Variable_Tests	( int&, int& );
	friend void		VarArray_Tests	( int&, int& );
	friend void		VarBool_Tests	( int&, int& );
	friend void		VarChar_Tests	( int&, int& );
	friend void		VarColor_Tests	( int&, int& );
	friend void		VarDbl_Tests	( int&, int& );
	friend void		VarInt_Tests	( int&, int& );
	friend void		VarStr_Tests	( int&, int& );
	friend void		VarTree_Tests	( int&, int& );
#endif


	friend class	UVarBool;
	friend class	UVarChar;
	friend class	UVarColor;
	friend class	UVarDouble;
	friend class	UVarInt;
	friend class	UVarString;
	friend class	UVarAry;
	friend class	UVarTree;




#	include		"var/uservar_flags.h"


	/**
	 *	@brief	callback informed when the value changes
	 */
	typedef	void	(*kbUserVarChange)	(
			const UserVar *		the_change	/**< the variable that's changing */
			);


	/**
	*	@brief	callback asked to confirm the value changes
	*
	*	the callback will copy the content of the new_v onto the variable
	*	if it is considered ok and returns true; if the new value is not
	*	ok then the variable is left unchanged and false is returned
	*/
	typedef	bool	(*kbUserVarValidate)	(
			UserVar *			the_var,	/**< the variable that's changing */
			const VarStore &	new_v		/**< the change; if ok this is copied in the_var */
			);



	/*  DEFINITIONS    ===================================================== */
	//
	//
	//
	//
	/*  DATA    ------------------------------------------------------------ */




	/**
	*	@brief	 the string representing the name of this variable
	*/
	QString			name_;


	/**
	*	@brief	 various information packed here
	*/
	Flags			flg_;


protected:

	/**
	*	@brief	 the actual data
	*/
	VarStore		data_;


	union	{

		/**
		*	@brief	 the callback that is informed about changes to this variable or NULL
		*/
		kbUserVarChange				kb_;

		/**
		*	@brief	 pointer to array of calbacks
		*/
		QList<kbUserVarChange>	*	pkb_;
	};


	/**
	*	@brief	 the callback asked to validate the new value
	*/
	kbUserVarValidate				val_;




	/*  DATA    ============================================================ */
	//
	//
	//
	//
	/*  FUNCTIONS    ------------------------------------------------------- */



private:

	/**
	*	@brief	used by constructors to perform common initialisation
	*/
	void		construct		( Module * mdl );


public:

	/**
	*	@brief	 Constructor; creates unnamed variable in default session
	*/
	UserVar		( void );


	/**
	*	@brief	 Constructor; named variable in specified module
	*
	*	@param	v_name	name to assign to variable
	*	@param	mdl		module where to create; if NULL is created in default
	*	session, default module
	*/
	UserVar		(
			QString				v_name,
			Module *			mdl = NULL
			);

	/**
	*	@brief	 Constructor; named variable in specified session, default module
	*
	*	@param	v_name	name to assign to variable
	*	@param	trg_ss	the variable is created in default module in this Session
	*	if NULL is created in default Session, default module
	*/
	UserVar		(
			QString				v_name,
			Session *			trg_ss
			);

	/**
	*	@brief	 Constructor; named variable in specified module
	*
	*	@param	v_name		name to assign to variable
	*	@param	flgs		attributes that will be directly copied to variable
	*	@param	kb_init		callback informed about changes
	*	@param	validator	callback asked to validate changes
	*	@param	mdl			module where to create; if NULL is created in default
	*	session, default module
	*/
	UserVar		(
			QString				v_name,
			Flags				flgs,
			kbUserVarChange		kb_init = NULL,
			kbUserVarValidate	validator = NULL,
			Module *			mdl = NULL
			);

	/**
	*	@brief	 Constructor; named variable in specified session, default module
	*
	*	@param	v_name		name to assign to variable
	*	@param	flgs		attributes that will be directly copied to variable
	*	@param	kb_init		callback informed about changes
	*	@param	validator	callback asked to validate changes
	*	@param	trg_ss	the variable is created in default module in this Session
	*	if NULL is created in default Session, default module
	*/
	UserVar		(
			Session *			trg_ss,
			QString				v_name,
			Flags				flgs,
			kbUserVarChange		kb_init = NULL,
			kbUserVarValidate	validator = NULL
			);



	/**
	*	@brief	 destructor;
	*/
	virtual				~UserVar		( void );


#	include	"var/uservar_basic.h"



	/**
	*	@brief	get the basic type of this storage
	*/
	inline StoreType	basicType		( void )
	{ return data_.basicType(); }


	/**
	*	@brief	get the composite type of this storage
	*/
	inline StoreType	compositeType	( void )
	{ return data_.compositeType(); }






	/** @name Callbacks
	  *	Each variable may have one or more callbacks associated with it
	  * that is used to validate the input. The function follows kbValidate
	  *	type and recieves both the unmodified and modified version of
	  *	the variable (the latter one is a temporary variable produced
	  *	by the caller and modifications on this one are not preserved).
	  *	The callback only has to return true to allow the change or false to
	  *	deny it. The actual change is performed by the caller.
	 */
	//@{


	/**
	*	@brief	 tell if a variable has at least one callback
	*/
	inline bool		hasCallback				( void ) const
	{ return kb_ != NULL; }


	/**
	*	@brief	 tell if a variable has more than one callbacks
	*/
	inline bool		hasMultipleCallbacks	( void ) const
	{ return hasCallback() && ( ( flg_ & FL_MULTIPLE_KB ) != 0 ); }


	/**
	*	@brief	 add a callback to this variable
	*/
	void			addCallback				( kbUserVarChange new_kb );


	/**
	*	@brief	remove a ccallback from this variable
	*
	*	@return true if the callback was found, false otherwise
	*/
	bool			delCallback				( kbUserVarChange the_kb );


	/**
	*	@brief	set the validator for this variable
	*/
	inline void		setValidator			( kbUserVarValidate new_kb )
	{ val_ = new_kb; }

	/**
	*	@brief	tells if variable has validator callback
	*/
	inline bool		hasValidator			( void )
	{ return  val_ != NULL; }


	/**
	*	@brief	validator callback
	*/
	inline kbUserVarValidate		validator			( void )
	{ return  val_; }


	/**
	*	@brief	signalChange tells all interested parties that the variable changed
	*/
	void			signalChange			( void );


	//@}



	/**
	*	@brief	change the value of the variable; this is a top level method
	*
	*	The methods call the validators if present and informs the callbacks
	*/
	bool			changeVal		( const VarStore & new_v );



	//@{
	//!	change the value of the variable; this is a top level method
	//!
	//!	The methods call the validators if present and informs the callbacks

	inline bool		changeVal		( const qint64 new_val )
	{ return changeVal( VarStore(new_val) ); }

	inline bool		changeVal		( const int new_val )
	{ return changeVal( VarStore(new_val) ); }

	inline bool		changeVal		( const bool new_val )
	{ return changeVal( VarStore(new_val) ); }

	inline bool		changeVal		( const char new_val )
	{ return changeVal( VarStore(new_val) ); }

	inline bool		changeVal		( const QRgb new_val )
	{ return changeVal( VarStore(new_val) ); }

	inline bool		changeVal		( const QColor new_val )
	{ return changeVal( VarStore(new_val) ); }

	inline bool		changeVal		( const QString new_val )
	{ return changeVal( VarStore(new_val) ); }

	inline bool		changeVal		( const char * new_val )
	{ return changeVal( VarStore(new_val) ); }

	inline bool		changeVal		( const qreal new_val )
	{ return changeVal( VarStore(new_val) ); }

	//@}



	//@{
	//! set the variable to a definitive value; this call skips the validator
	//! and the callback

	inline void	setVal( const VarStore & new_val )
	{ resetContent(); data_ = new_val; }


	inline void	setVal( const qint64 new_val )
	{ resetContent(); data_.set( new_val ); }

	inline void	setVal( const int new_val )
	{ resetContent(); data_.set( new_val ); }

	inline void	setVal( const bool new_val )
	{ resetContent(); data_.set( new_val ); }

	inline void	setVal( const char new_val )
	{ resetContent(); data_.set( new_val ); }

	inline void	setVal( const QRgb new_val )
	{ resetContent(); data_.set( new_val ); }

	inline void	setVal( const QColor new_val )
	{ setVal( new_val.rgb() ); }

	void		setVal( const QString new_val )
	{ resetContent(); data_.set( new_val ); }

	void		setVal(const char * new_val)
	{ setVal( QString( new_val ) ); }

	inline void	setVal( const qreal new_val )
	{ resetContent(); data_.set( new_val ); }

	inline void	setValChar( const int new_val )
	{ resetContent(); data_.setChar( new_val ); }

	inline void	setValColor( const uint new_val )
	{ resetContent(); data_.setColor( new_val ); }


	/* arrays */

	void		setVal	( const int * p_list, int cnt_lst );

	void		setVal	( const qint64 * p_list, int cnt_lst );

	void		setVal	( const double * p_list, int cnt_lst );

	void		setVal	( const bool * p_list, int cnt_lst );

	void		setVal	( const char * p_list, int cnt_lst );

	void		setVal	( const QChar * p_list, int cnt_lst );

	void		setVal	( const QRgb * p_list, int cnt_lst );

	void		setVal	( const QStringList p_list );

	/**
	*	@brief initialises the array to specified size without filling it
	*
	*	Each element in the newly created array is set to a default value.
	*
	*	@param el_cnt	number of elements to create
	*	@param ary_ty	type of each element
	*/
	void		setValArray	( int el_cnt, StoreType ary_ty );

	/**
	*	@brief creates a character arry from provided string
	*	@param src	the source string that is converted to array
	*/
	void		setCharArray	( QString src );

	//@}



	/** @name Retrieving common values
	*
	*/
	//@{

	bool				convToBool		( bool * b_ok, VarStore * uv = NULL );

	qint16				convToShort		( bool * b_ok, VarStore * uv = NULL );

	qint32				convToInt		( bool * b_ok, VarStore * uv = NULL );

	qint64				convToLong		( bool * b_ok, VarStore * uv = NULL );

	qint8				convToByte		( bool * b_ok, VarStore * uv = NULL );

	qreal				convToDouble	( bool * b_ok, VarStore * uv = NULL );

	QStringList			convToStrLst	( bool * b_ok, VarStore * uv = NULL );

	QString				convToStr		( bool * b_ok, VarStore * uv = NULL );

	//@}



	/**
	*	@brief	DefVar interface for this variable
	*
	*	Any variable has an associated DefVar instance; when the VarType is
	*	VTY_DEFVAR an DefVar is stored inside VarStore; when VarType is any
	*	other StoreType a prebuild instance is used.
	*/
	DefVar *	interface			( void );



	/**
	*	@brief	the variable is set to the ground state
	*
	*	On this state there are no resources allocated and the type of the
	*	embedded VarStore set to VTY_INTEGER and a value of 0
	*/
	void		resetContent		( void );


	/**
	*	@brief	describes in a short string the type of undelying storage
	*
	*/
	QString 	typeName			( void );


	/**
	*	@brief	save the content of the variable
	*
	*	@return	true if variable was saved, false otherwise
	*/
	bool		save				(
			PgSave::IPgSave & stg
			);


	/**
	*	@brief	load the content of the variable
	*
	*	@return	true if variable was loaded, false otherwise
	*/
	bool		load				(
			PgSave::IPgSave & targ_i
			);



public:


	/**
	*	@brief	loads a variable from provided interface
	*
	*	the interface is assumed to have the current path set to the folder
	*	that contains the variable; the function will enter the variable folder
	*	itself
	*
	*	@param	s_name		the name of the variable
	*	@param	targ_mdl	where to append this variable
	*	@param	targ_i		the interface to use
	*
	*	@return		NULL if failure, the pointer to newly created variable
	*/
	static UserVar *		load					(
			QString				s_name,
			Module *			targ_mdl,
			PgSave::IPgSave &	targ_i
			);


private:

	inline void		setFlag				( int new_flg )
	{ flg_ = static_cast<Flags> (flg_ | new_flg ); }

	inline void		resetFlag			( int new_flg )
	{ flg_ = static_cast<Flags> (flg_ & (~new_flg) ); }


	/*  FUNCTIONS    ======================================================= */
	//
	//
	//
	//

};	/*	class UserVar	*/

/*  CLASS    =============================================================== */
//
//
//
//

#ifdef	PGSCRIPT_TESTS
void				Variable_Tests					(
		int & performed,
		int & succeded
		);
#endif



}	//	namespace	PgScr

#endif // __USERVAR_INC__
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
